package me.ywd.seckill.service;

import me.ywd.seckill.dao.ProductDao;
import me.ywd.seckill.dao.SeckilledDao;
import me.ywd.seckill.dto.Exposer;
import me.ywd.seckill.dto.SeckillExecution;
import me.ywd.seckill.entity.Product;
import me.ywd.seckill.entity.Seckilled;
import me.ywd.seckill.exception.RepeatKillException;
import me.ywd.seckill.exception.SeckillCloseException;
import me.ywd.seckill.exception.SeckillException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import java.util.Date;
import java.util.List;

/**
 * @Author Ye_Wenda
 * @Date 2016/11/9 0009
 */
@Service
public class SeckillServiceImpl implements SeckillService {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private ProductDao productDao;
    @Autowired
    private SeckilledDao seckilledDao;

    private final String slat = "diej(*&^()(*Eell";

    @Override
    public List<Product> getProducts() {
        return productDao.findAll();
    }

    @Override
    public Product getProduct(long id) {
        return productDao.findById(id);
    }

    @Override
    public List<Seckilled> getKilled(String phone) {
        return seckilledDao.findByPhone(phone);
    }

    @Override
    public List<Seckilled> getKilled(int productId) {
        return seckilledDao.findByProduct(productId);
    }

    @Override
    public Exposer exportSeckillUrl(long productId) {
        Product product = productDao.findById(productId);
        if (product == null) {
            return new Exposer(false, productId);
        }
        Date startTime = product.getStartTime();
        Date endTime = product.getEndTime();
        Date now = new Date();
        if (now.getTime() < startTime.getTime() || now.getTime() > endTime.getTime()) {
            return new Exposer(false, productId, now.getTime()
                    ,startTime.getTime(), endTime.getTime());
        }
        // generate an irreversible string.
        String md5 = getMD5(productId);
        return new Exposer(true, md5, productId);
    }

    /**
     * 1.Make sure the execution time of the transactional method shortest.
     *   Don't do internet action(for instance, RPC/HTTP), best practice is exclude them from transactional method.
     * @param productId
     * @param phone
     * @param md5
     * @return
     * @throws SeckillException
     * @throws RepeatKillException
     * @throws SeckillCloseException
     */
    @Override
    @Transactional
    public SeckillExecution executeSeckill(long productId, String phone, String md5)
            throws SeckillException, RepeatKillException, SeckillCloseException {
        try {
            if (md5 == null || !md5.equals(getMD5(productId))) throw new SeckillException("Second Kill data re-writed!");
            // Real kill process:
            Date now = new Date();
            int updateCount = productDao.reduceProduct(productId, now);
            // Nothing updated.
            if (updateCount <= 0) throw new SeckillCloseException("Second kill closed.");
            // Record order info
            int insertCount = seckilledDao.add(productId, phone);
            if (insertCount <= 0) throw new RepeatKillException("Please do not kill again!");
            Seckilled seckilled = seckilledDao.find(productId, phone);
            return new SeckillExecution(productId, 1, "Killing Spirit");
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            // Spring 声明式事物帮我们回滚
            throw new SeckillException("Second kill service inner error.");
        }
    }

    private String getMD5(long productId) {
        String base = productId + "/" + slat;
        String md5 = DigestUtils.md5DigestAsHex(base.getBytes());
        return md5;
    }
}
